#include "Parser.h"
#include <sstream>
#include <stack>
#include <set>

using namespace std;

bool IsMeaninglessToken(const string &t) {
	return (t == "lineterminatorcarrier" || t == "lineterminator" || t == "comment" || t == "whitespace");
}

Parser::Parser(const char *parsetablefilename, const char *programfilename, const char *dfaFilename) : _isAValidProgram(false) {
	string classname = "/\\" + string(programfilename);
	if (classname.substr(classname.size() - 5) != ".java") return;

	ifstream ptstream(parsetablefilename);
	if (!ptstream.is_open()) throw ParseTableFileNotFound();

	static set<string> reservedStrings; //Set of keywords and things like true, false, null and operators
	static Rule *rules;
	static State *states;

	static bool initialised = false;
	if (!initialised) {

		//Running through the input
		int i;
		ptstream >> i; //Number of terminals
		string s;
		getline(ptstream, s);

		//Put the terminals into the reserved set
		for (; i > 0; --i) {
			getline(ptstream, s);
			reservedStrings.insert(s);
		}

		ptstream >> i; //Number of non-terminals
		for (++i; i >= 0; --i) getline(ptstream, s);
		ptstream >> i; //Number of Rules
		getline(ptstream, s);

		rules = new Rule[i];

		for (int n = 0; n < i; ++n) {
			getline(ptstream, s);
			stringstream stream(s);
			stream >> s;
			rules[n].lefthand = s;
			unsigned int size = 0;
			for (; stream >> s; ++size);
			rules[n].righthandSize = size;
		}

		// THIS ASSUMES THE PROF'S CODE RETURNS CORRECT OUTPUT. IE. NO ERROR CHECKING
		ptstream >> i;

		states = new State[i];

		unsigned int c;
		ptstream >> c;

		string a;
		for (unsigned int n = 0; n < c; ++n) {
			ptstream >> i;
			State &state = states[i];
			ptstream >> s >> a >> i;

			if (a.compare("shift") == 0) state.transitions[s] = i;
			else state.reductions[s] = i;
		}

		initialised = true;
	}
	//Parse the code
	Lexer lexer(programfilename, dfaFilename);

	stack<unsigned int> statestack;
	stack<ParseNode*> nodestack;

	Token nexttoken = Token("BOF");

	//Push in the first state
	statestack.push(states[0].transitions[nexttoken.type]);
	//And the first node
	nodestack.push(new LeafNode(nexttoken.type, nexttoken.lexeme));

	unsigned int currentstate = statestack.top();

	//Get the filename
	classname = classname.substr(classname.find_last_of("/\\")+1);
	//remove the .java extension
	classname = classname.substr(0, classname.size() - 5);

	set<string>::iterator reservedStringsEnd = reservedStrings.end();
	//Keep reading while we haven't yet pushed in the EOF token
	for (nexttoken = lexer.PopNext(); nodestack.top()->Type() != "EOF"; nexttoken = lexer.PopNext()) {

		if (IsMeaninglessToken(nexttoken.type)) continue;
		//Type twiddling
		else if (reservedStrings.find(nexttoken.lexeme) != reservedStringsEnd) nexttoken.type = nexttoken.lexeme;

		for (map<string, unsigned int>::iterator reduction = states[currentstate].reductions.find(nexttoken.type); //Find the reduction rule
			reduction != states[currentstate].reductions.end();  //Check if there is a reduction to make
			reduction = states[currentstate].reductions.find(nexttoken.type)) { //Update the same way as init

			//Get the rule to reduce by
			Rule &r = rules[reduction->second];

			//Make a new node that will be the parent of the nodes in the rule (IE. Goal -> BOF CompilationUnit EOF, Goal is the parent and the 3 tokens are the children)
			ParentNode *parent = new ParentNode(r.righthandSize);

			//Do the necessary pops and update the parent with the children
			for (int i = r.righthandSize - 1; i >= 0; --i) {
				((*parent)[i] = nodestack.top())()->parent = parent;
				nodestack.pop();
				statestack.pop();
			}

			//Use the replacement to find the next state (this ;is guaranteed to work, if the grammar is correct. If we had a vp before the reduction, we maintain a vp)
			statestack.push(states[statestack.top()].transitions[r.lefthand]);
			nodestack.push(ParseNode::SetType(r.lefthand, parent));

			currentstate = statestack.top();
		}
		nodestack.push(ParseNode::SetType(nexttoken.type, new LeafNode(nexttoken.type, nexttoken.lexeme)));

		currentstate = statestack.top();
		map<string, unsigned int>::iterator transition = states[currentstate].transitions.find(nexttoken.type);
		if (transition == states[currentstate].transitions.end()) break; 

		//Push in the new state
		statestack.push(transition->second);

		currentstate = statestack.top();
	}

	//EOF was put on the stack right at the end; delete it and take it off
	delete nodestack.top();
	nodestack.pop();
	
	//Make sure the stack looks correct
	switch (nodestack.size()) {
		case 2: //Stack is <BOF, CompilationUnit, EOF>
			if (nodestack.top()->Type() == "CompilationUnit") {
				_root = (ParentNode*)nodestack.top();
					
				CompressionVisitor cv;
				_root()->Accept(&cv);
				
				FillVisitor fv;
				_root()->Accept(&fv);
				
				WeedingVisitor visitor = WeedingVisitor(classname);
				_root()->Accept(&visitor);
				
				_isAValidProgram = visitor.IsValid();
			} else delete nodestack.top();
			nodestack.pop();
		case 1: //Stack is <BOF, EOF>
			delete nodestack.top();
			break;
		default :
			for (unsigned int d = nodestack.size(); d > 0; --d) {
				delete nodestack.top();
				nodestack.pop();
			}
	}
}
